# 
#  Copyright (C) 2005 Christopher J. Stawarz <cstawarz@cfa.harvard.edu>
# 
#  This file is part of PySL.
# 
#  PySL is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
# 
#  PySL is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
# 
#  You should have received a copy of the GNU General Public License
#  along with PySL; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#


"""
This module provides a simple way of (nearly) duplicating slsh's runtime
environment in a PySL session.  The main benefit of doing this is that it
makes slsh's provide/require mechanism available.  However, the instrinsic
variables and functions defined by slsh (such as __argc, __argv, atexit(),
and exit()) are not made available.

When imported, the module load any files that slsh evaluates at startup
(including the user's slshrc file, if it exists), and prepends slsh's load
and module-import paths to the current paths.  If slsh is not available, an
ImportError will be raised.

The functions and data defined in this module are intended for internal use
only.  Calling code should do nothing with it other than import it.
"""


import os
from pysl import load_file, SLangFunction, SLangError


# Get functions for manipulating load and module-import paths
get_load_path   = SLangFunction('get_slang_load_path')
set_load_path   = SLangFunction('set_slang_load_path')
get_import_path = SLangFunction('get_import_module_path')
set_import_path = SLangFunction('set_import_module_path')


def path_to_reversed_list(p):
   """
   Given "a:b:c" (where ':' is really os.pathsep), return ['c','b','a']:

   >>> path_to_reversed_list("a:b:c")
   ['c', 'b', 'a']
   >>> path_to_reversed_list("")
   []
   """

   if not p:  return []
   p = p.split(os.pathsep)
   p.reverse()
   return p


def merge_paths(p1, p2):
   """
   Given p1="c:b:a" and p2="e:b:d", return "e:d:c:b:a" (where ':' is really
   os.pathsep):

   >>> merge_paths("c:b:a", "e:b:d")
   'e:d:c:b:a'
   >>> merge_paths("b:a", "e:b:d")
   'e:d:b:a'
   >>> merge_paths("b:a", "e:d")
   'e:d:b:a'
   >>> merge_paths("", "e:d")
   'e:d'
   >>> merge_paths("b:a", "")
   'b:a'
   >>> merge_paths("c:b:a", "b")
   'c:b:a'
   >>> merge_paths("", "")
   ''
   """

   p1 = path_to_reversed_list(p1)
   p2 = path_to_reversed_list(p2)

   for dir in p2:
      if not dir in p1:
	 p1.append(dir)

   p1.reverse()
   return os.pathsep.join(p1)


def load_slsh_config_files():
   """
   Load any files that slsh evaluates at startup (including the user's
   slshrc, if it exists), and prepend slsh's load and module-import paths to
   the current paths.  If slsh is not available, an IOError will be raised.

   >>> require = SLangFunction('require')
   >>> try:
   ...    require('not_a_file')
   ... except SLangError, err:
   ...    print str(err)[1:]
   ...
     S-Lang Error: Object not opened: Unable to locate not_a_file on load path
     S-Lang Error: Object not opened: Error while executing require
   """

   # Fire up slsh and have it print the files it loads at startup (by giving
   # the '-v' option) and it's load and module-import paths
   slsh_in, slsh_out, slsh_err = os.popen3('slsh -v')
   print >> slsh_in, r'() = printf("%s\n", get_slang_load_path());'
   print >> slsh_in, r'() = printf("%s\n", get_import_module_path());'
   slsh_in.close()
   slsh_output = slsh_out.readlines()
   slsh_out.close()
   slsh_err.close()

   # If we didn't get at least two lines, something went wrong (probably slsh
   # isn't in the user's path)
   if len(slsh_output) < 2:  raise IOError

   # Prepend slsh's load and import paths to the current ones so that loads
   # and imports in the startup files work
   load_path = merge_paths(get_load_path(), slsh_output[-2].rstrip())
   import_path = merge_paths(get_import_path(), slsh_output[-1].rstrip())
   set_load_path(load_path)
   set_import_path(import_path)

   # Load slsh's startup files
   for line in slsh_output[:-2]:
      if not line.startswith('Loading '):
         continue
      load_file((line.split(' ')[1]).rstrip())

   # Loading the slsh config files after setting the paths causes some
   # directories to appear twice, so just use the values we got before
   set_load_path(load_path)
   set_import_path(import_path)


try:
   load_slsh_config_files()
except IOError, SLangError:
   # Either slsh isn't present, or a S-Lang error occurred during file
   # loading
   raise ImportError('unable to load slsh extensions')


#
# doctest stuff
#

def _test():
   import doctest, sys
   return doctest.testmod(sys.modules["__main__"])

if __name__ == "__main__":
   _test()

